package com.jkgh.test;

import com.jkgh.asin.StateMachine;
import com.jkgh.asin.buffers.DataConsumer;
import com.jkgh.remedium.RemediumConstants;
import com.jkgh.remedium.RemediumHost;
import com.jkgh.remedium.RemediumServer;
import com.jkgh.remedium.Returner;
import com.jkgh.remedium.objects.RemediumMethodObject;
import com.jkgh.remedium.objects.messages.RemediumInMessage;
import com.jkgh.remedium.objects.messages.out.RemediumS2CCaller;
import com.jkgh.remedium.objects.messages.out.RemediumS2CIntResponder;
import com.jkgh.remedium.states.Callback;
import com.jkgh.remedium.states.Future;
import com.jkgh.remedium.states.GatheringState;
import com.jkgh.remedium.tools.RemediumTools;

/**
 * A server controller for something.
 */
public abstract class MainServerController extends RemediumMethodObject {

	/**
	 * Does command.
	 * 
	 * @param a A command to do.
	 */
	public abstract void doIt(String a);
	
	/**
	 * Does the usual thing.
	 */
	public abstract void doTheUsual();
	
	/**
	 * Does the ultimate thing.
	 * 
	 * @param ms Other parties.
	 * @param sh Command id.
	 * @param returner Should return: A new id for things. 
	 */
	public abstract void doSomethingElseWith(MainServerController[] ms, int sh, Returner<Integer> returner);
	
	// --------------------------------------------------------------------------------
	
	public MainServerController(RemediumServer server) {
		super(server);
	}

	protected MainServerController(int hostID, int objectID, RemediumServer server) {
		super(hostID, objectID, server);
	}

	@Override
	public void call(final StateMachine machine, final RemediumInMessage message, final int callID, int methodID, final Callback callback) {
		switch (methodID) {
		
			case 0:
				RemediumTools.consumeString(machine, new Future<String>() {
					
					@Override
					public void goWith(String aa) {
						doIt(aa);
						callback.go();
					}
				});
				break;
				
			case 1:
				doTheUsual();
				callback.go();
				break;
				
			case 2:
				MainServerController.consumeArray(machine, message, new Future<MainServerController[]>() {
					
					@Override
					public void goWith(final MainServerController[] cms) {
						RemediumTools.consumeInt(machine, new Future<Integer>() {

							@Override
							public void goWith(Integer csh) {
								doSomethingElseWith(cms, csh, new Returner<Integer>() {

									@Override
									public void returned(Integer r) {
										RemediumS2CIntResponder cresponder = new RemediumS2CIntResponder(callID, r);
										cresponder.produce(machine);
									}
								});
								callback.go();
							}
						});
					}
				});
				break;
		}
	}

	public static void consume(final StateMachine machine, final RemediumInMessage message, final Future<MainServerController> future) {
		machine.setCurrentState(new GatheringState(machine, 1) {
			
			@Override
			protected void onNeededBytesAvailable() {
				switch (machine.produceByte()) {

					case RemediumConstants.NULL:
						future.goWith(null);
						break;
						
					case RemediumConstants.HISTORY:
						future.goWith((MainServerController) message.fromHistory(machine.produceInt()));
						break;
						
					case RemediumConstants.VALUE:
						consumeContent(machine, message, future);
						break;
				}
			}
		});
	}

	private static void consumeArray(final StateMachine machine, final RemediumInMessage message, final Future<MainServerController[]> future) {
		machine.setCurrentState(new GatheringState(machine, 4) {
			
			@Override
			protected void onNeededBytesAvailable() {
				final int length = machine.produceInt();
				if (length == -1) {
					future.goWith(null);
				} else if (length == 0) {
					future.goWith(new MainServerController[0]);
				} else {
					machine.setCurrentState(new GatheringState(machine, 8*length) {
						
						@Override
						protected void onNeededBytesAvailable() {
						
							MainServerController[] result = new MainServerController[length];
							for (int i=0; i<length; ++i) {
								result[i] = consumeData(machine, message);
							}
							future.goWith(result);
						}
					});
				}
			}
		});
	}
	
	private static void consumeContent(final StateMachine machine, final RemediumInMessage message, final Future<MainServerController> future) {
		machine.setCurrentState(new GatheringState(machine, 8) {
			
			@Override
			protected void onNeededBytesAvailable() {
				
				MainServerController methodObject = consumeData(machine, message);
				future.goWith(methodObject);
			}
		});
	}
	
	private static MainServerController consumeData(StateMachine machine, RemediumInMessage message) {

		int hostID = machine.produceInt();
		int objectID = machine.produceInt();
		
		RemediumHost host = message.getServer().getHost(hostID);
		MainServerController methodObject = (MainServerController) host.getMethodObject(objectID);
		if (methodObject == null) {
			methodObject = new MainServerControllerRedirector(hostID, objectID, message.getServer());
			host.registerMethodObject(objectID, methodObject);
		}
		
		message.toHistory(methodObject);
		
		return methodObject;
	}

	private static class MainServerControllerRedirector extends MainServerController {

		private final RemediumHost host;

		protected MainServerControllerRedirector(int hostID, int objectID, RemediumServer server) {
			super(hostID, objectID, server);
			
			this.host = server.getHost(hostID);
		}

		@Override
		public void doIt(final String a) {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 0, 0) {
				
				@Override
				protected void produceArguments(DataConsumer consumer) {
					RemediumTools.produceString(consumer, a);
				}
			};
			caller.produce(host.getMachine());
		}

		@Override
		public void doTheUsual() {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 1, 0) {
				
				@Override
				protected void produceArguments(DataConsumer consumer) {
				}
			};
			caller.produce(host.getMachine());
		}

		@Override
		public void doSomethingElseWith(final MainServerController[] ms, final int sh, Returner<Integer> returner) {
			RemediumS2CCaller caller = new RemediumS2CCaller(objectID, 2, host.registerCall(returner)) {
				
				@Override
				protected void produceArguments(DataConsumer consumer) {
					RemediumTools.produceObjectArray(consumer, this, ms);
					consumer.consumeInt(sh);
				}
			};
			caller.produce(host.getMachine());
		}
		
	}
}
